'use client';

import type {
  IDraft,
  IHistory,
  IPostEditInfo,
  IPostNewInfo,
} from '@/interfaces';
import type { IPostTemplate } from '@/interfaces';
import {
  type ChangeEvent,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';
import { PostEditContext } from '@/contexts/post-edit';
import classNames from 'classnames';
import Nodata from '@/app/[locale]/common/nodata/nodata';
import Outline from '@/app/[locale]/common/post/outline';
import ContentHtml from '@/app/[locale]/common/content/html';
import { getDiffData, isEmpty, isNum, isValidLink } from '@/lib/tool';
import {
  handleEditorBeforeunload,
  handleEditorSave,
  handleEditorStatusChanges,
} from '@/lib/editor/handle';
import useToast from '@/hooks/useToast';
import dynamic from 'next/dynamic';
import EditorSuspense from '@/app/[locale]/posts/[id]/edit/editor/load';
import { getEditorContentHtml, setEditorContentHtml } from '@/lib/editor';
import { useMutation, useQuery } from '@tanstack/react-query';
import {
  queryDraft,
  queryHistory,
  queryPostTemplateBySection,
  updatePostEditInfo,
  updatePostNewInfo,
} from '@/services/api';
import sanitizeHtml from 'sanitize-html';
import type { TFileManagerTranslatedField } from '@/app/[locale]/file/file-manager';
import type { TEditorDynamicTranslatedField } from '@/app/[locale]/posts/[id]/edit/editor/editor';
import { useSearchParams } from 'next/navigation';

export type TEditBodyTranslatedField =
  | 'namePlaceholder'
  | 'contentPlaceholder'
  | 'preview'
  | 'cancelPreview'
  | 'save'
  | 'editorDoesNotExist'
  | 'failedToInitializeEditor'
  | 'saveCompleted'
  | 'saveCompletedAboutToJump'
  | 'titleRequired'
  | 'sectionIdRequired';

const DynamicEditor: any = dynamic(() => import('../editor/editor'), {
  loading: () => <EditorSuspense />,
  ssr: false,
});

export default function EditPage({
  source,
  translatedFields = {
    namePlaceholder: '请输入标题',
    contentPlaceholder: '请输入内容',
    preview: '预览',
    cancelPreview: '取消预览',
    save: '保存',
    editorDoesNotExist: '编辑器不存在',
    failedToInitializeEditor: '初始化编辑器失败',
    saveCompleted: '保存完成',
    saveCompletedAboutToJump: '保存完成，即将跳转',
    titleRequired: '标题不能为空',
    sectionIdRequired: '内容未选择或者为空',
  },
  editorTranslatedFields,
  fileManagerTranslatedFields,
}: {
  source: IPostEditInfo | IPostNewInfo;
  translatedFields?: Record<TEditBodyTranslatedField, any>;
  editorTranslatedFields?: Record<TEditorDynamicTranslatedField, any>;
  fileManagerTranslatedFields?: Record<TFileManagerTranslatedField, any>;
}) {
  const context = useContext(PostEditContext);
  const { form, setForm } = context;
  const isEdit = 'basic' in source;
  const id = isEdit ? (source as IPostEditInfo).basic.id : undefined;
  const [isPreview, setIsPreview] = useState(false);
  const [previewContent, setPreviewContent] = useState('');
  const { show } = useToast();
  const editorRef = useRef<any>();
  const [isActive, setIsActive] = useState(false);
  const [isSaving, setIsSaving] = useState(false);
  const isDirty = useRef(false);
  const isLoadingSave = useRef(false);
  const [isReady, setIsReady] = useState(false);
  const searchParams = useSearchParams();
  const draftId = searchParams.get('draft');
  const historyId = searchParams.get('history');
  const templateId = searchParams.get('template');
  const templateSId = searchParams.get('template-sid');

  useEffect(() => {
    return () => {
      setForm((prevState) => {
        prevState.content = getContent();
        return prevState;
      });
    };
  }, []);
  useEffect(() => {
    const selector = document.querySelector('.yw-navbar-layout');
    if (selector) {
      selector.classList.remove('sticky-top');
    }

    return () => {
      if (selector) {
        selector.classList.add('sticky-top');
      }
    };
  }, []);

  const queryDraftQuery = useQuery(
    ['/forum', '/drafts', draftId],
    async (context) => {
      return (await queryDraft({
        id: context.queryKey[2] + '',
      })) as IDraft;
    },
    {
      enabled: isNum(draftId),
    },
  );
  const queryHistoryQuery = useQuery(
    ['/forum', '/histories', historyId],
    async (context) => {
      return (await queryHistory({
        id: context.queryKey[2] + '',
      })) as IHistory;
    },
    {
      enabled: isNum(historyId),
    },
  );
  const queryPostTemplateBySectionQuery = useQuery(
    ['/forum', '/templates', '/sections', templateSId],
    async (context) => {
      return (await queryPostTemplateBySection({
        id: context.queryKey[3],
      })) as IPostTemplate[];
    },
    {
      enabled: isNum(templateSId),
    },
  );

  const updatePostEditInfoMutation = useMutation(updatePostEditInfo);
  const updatePostNewInfoMutation = useMutation(updatePostNewInfo);

  useEffect(() => {
    const data = queryDraftQuery.data;
    if (isReady && data) {
      const editor = editorRef.current;
      const { name, overview, content } = data;
      const obj: any = {};

      if (name) {
        obj.name = name;
      }
      if (overview) {
        obj.overview = overview;
      }
      if (content && editor) {
        obj.content = content;
        setEditorContentHtml(editor, content);
      }

      setForm((prevState) => ({ ...prevState, ...obj }));
    }
  }, [isReady, queryDraftQuery.data]);
  useEffect(() => {
    const data = queryHistoryQuery.data;
    if (isReady && data) {
      const editor = editorRef.current;
      const { name, overview, content } = data;
      const obj: any = {};

      if (name) {
        obj.name = name;
      }
      if (overview) {
        obj.overview = overview;
      }
      if (content && editor) {
        obj.content = content;
        setEditorContentHtml(editor, content);
      }

      setForm((prevState) => ({ ...prevState, ...obj }));
    }
  }, [isReady, queryHistoryQuery.data]);
  useEffect(() => {
    const data = queryPostTemplateBySectionQuery.data;
    if (isReady && templateId && data) {
      const editor = editorRef.current;
      const { name, overview, content } =
        data.find((item) => item.id + '' === templateId) ?? {};
      const obj: any = {};

      if (name) {
        obj.name = name;
      }
      if (overview) {
        obj.overview = overview;
      }
      if (content && editor) {
        obj.content = content;
        setEditorContentHtml(editor, content);
      }

      setForm((prevState) => ({ ...prevState, ...obj }));
    }
  }, [isReady, templateId, queryPostTemplateBySectionQuery.data]);

  function onChange(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name as 'name';
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  function getContent(data?: any) {
    return getEditorContentHtml(editorRef.current, data);
  }

  function onClickPreview() {
    setIsPreview(true);
    setPreviewContent(getContent());
  }

  function onClickCancelPreview() {
    setIsPreview(false);
    setPreviewContent('');
  }

  function checkForm() {
    const { name, sectionId } = form;
    if (!name.trim()) {
      throw translatedFields.titleRequired;
    }

    if (!sectionId) {
      throw translatedFields.sectionIdRequired;
    }
  }

  async function onClickSave() {
    const editor = editorRef.current;
    if (!editor) {
      show({
        type: 'DANGER',
        message: translatedFields.editorDoesNotExist,
      });
      return;
    }

    await handleEditorSave({
      editor,
      isDirty,
      setIsActive,
      setIsSaving,
      callback: onClickSaveCore,
    });
  }

  async function onClickSaveCore({
    data,
    reset,
  }: {
    data: string;
    reset: () => void;
  }) {
    try {
      isLoadingSave.current = true;

      checkForm();
      const body = handleBody(data);
      if (!body) {
        return;
      }

      if (isEdit) {
        await updatePostEditInfoMutation.mutateAsync({
          id: id,
          data: body,
        });

        show({
          type: 'SUCCESS',
          message: translatedFields.saveCompleted,
        });
      } else {
        const { headers } = (await updatePostNewInfoMutation.mutateAsync({
          data: body,
        })) as Response;

        show({
          type: 'SUCCESS',
          message: translatedFields.saveCompletedAboutToJump,
        });

        const href = headers.get('location');

        setTimeout(() => {
          if (href) {
            location.href = href;
          } else {
            location.href = '/';
          }
        }, 1500);
      }
    } catch (e) {
      if (isEdit) {
        updatePostEditInfoMutation.reset();
      } else {
        updatePostNewInfoMutation.reset();
      }

      show({
        type: 'DANGER',
        message: e,
      });
    } finally {
      isLoadingSave.current = false;
      reset();
    }
  }

  function getImages(content: string) {
    if (!content) {
      return [];
    }

    return (content.match(/<img.*?>/g) || [])
      .filter((item) => item.includes('width=') && item.includes('height='))
      .map((item) => {
        const exec = /src="([^"\s]+)"/g.exec(item);
        return exec ? exec[1].trim() : null;
      })
      .filter((item) => item)
      .filter((value, index, array) => array.indexOf(value) === index);
  }

  function handleBody(data: string) {
    const body = getDiffData(context.differenceData);
    body.content = getContent(data);
    body.images = getImages(body.content);

    if (body.overview) {
      body.overview = sanitizeHtml(body.overview);
    }

    if (
      body.contentType === 'LINK' &&
      body.contentLink &&
      isValidLink(body.contentLink)
    ) {
      body.content = `<a href="${body.contentLink}" rel="noopener noreferrer" target="_blank">${body.contentLink}</a>`;
    } else if (body.content) {
      body.contentType = 'DEFAULT';
    } else {
      body.content = body.name;
      body.contentType = 'NONE';
    }

    if (body.otherStates && body.otherStates.includes('DEFAULT')) {
      body.otherStates = [];
      body.allow = [];
      body.block = [];
    }

    if (body.allow) {
      body.allow = body.allow
        .filter((value: string) => isNum(value))
        .map((value: string) => parseInt(value));
    }

    if (body.block) {
      body.block = body.block
        .filter((value: string) => isNum(value))
        .map((value: string) => parseInt(value));
    }

    if (isEmpty(body)) {
      show({
        type: 'SUCCESS',
        message: translatedFields.saveCompleted,
      });
      return;
    }

    return body;
  }

  function onReady({ editor }: { editor: any }) {
    handleEditorStatusChanges({
      editor,
      isDirty,
      setIsActive,
      setIsSaving,
    });
    handleEditorBeforeunload({ editor });
    setEditorContentHtml(editor, form.content);
    editorRef.current = editor;
    setIsReady(true);
  }

  return (
    <div className="card border-0">
      <div className="card-body d-flex flex-column gap-4">
        <div className="input-group mt-4" role="group" aria-label="buttons">
          <input
            type="text"
            name="name"
            value={form.name}
            className="form-control text-center"
            placeholder={translatedFields.namePlaceholder}
            onChange={onChange}
          />
          {isPreview ? (
            <button
              onClick={onClickCancelPreview}
              type="button"
              className="btn btn-outline-primary"
            >
              {translatedFields.cancelPreview}
            </button>
          ) : (
            <button
              onClick={onClickPreview}
              type="button"
              className="btn btn-outline-primary"
            >
              {translatedFields.preview}
            </button>
          )}

          <button
            disabled={!isActive || isSaving || isLoadingSave.current}
            onClick={onClickSave}
            type="button"
            className="btn btn-outline-primary"
          >
            {isSaving || isLoadingSave.current ? (
              <span
                className="spinner-border spinner-border-sm me-2"
                aria-hidden="true"
              ></span>
            ) : (
              <i className="bi bi-save me-2"></i>
            )}
            {translatedFields.save}
          </button>
        </div>

        <div
          className={classNames({
            'd-none': !isPreview,
            'mt-5 position-relative': isPreview,
          })}
        >
          {previewContent ? (
            <>
              <Outline content={previewContent} />
              <ContentHtml content={previewContent} />
            </>
          ) : (
            <Nodata />
          )}
        </div>

        <div
          className={classNames({
            'd-none': isPreview,
          })}
        >
          <DynamicEditor
            id={id}
            onReadyCallback={onReady}
            translatedFields={editorTranslatedFields}
            fileManagerTranslatedFields={fileManagerTranslatedFields}
          />
        </div>
      </div>
    </div>
  );
}
